/* *
 * @description:
 * @file: drag.ts
 * @author: Evan
 * @date: 2025.05.09 19:53:06
 * @week: 周五
 * @version: V
 * */

import type { Directive } from 'vue'
/* 拖拽指令 */
const DragMap = new Map()

const createEvent = (el: HTMLElement) => {
  const targetMap = DragMap.get(el)
  // 拖拽元素
  const dragEl = targetMap.dragEl
  // 移动元素
  const moveEL = targetMap.moveEL

  dragEl.style.cursor = 'move'
  // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle( dom元素, null );
  const sty = moveEL.currentStyle || window.getComputedStyle(moveEL, null)
  dragEl.onmousedown = (event: MouseEvent) => {
    // 鼠标按下，计算当前元素距离可视区的距离
    const disX = event.clientX - dragEl.offsetLeft
    const disY = event.clientY - dragEl.offsetTop
    // 获取到的值带px 正则匹配替换
    let styL, styT
    // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
    if (sty.left.includes('%')) {
      styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
      styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
    } else {
      styL = +sty.left.replace(/\px/g, '')
      styT = +sty.top.replace(/\px/g, '')
    }
    document.onmousemove = e => {
      // 通过事件委托，计算移动的距离
      const l = e.clientX - disX
      const t = e.clientY - disY
      dragEl.style.cursor = 'grabbing'
      // 移动当前元素
      moveEL.style.left = `${l + styL}px`
      moveEL.style.top = `${t + styT}px`
      // 将此时的位置传出去
      targetMap.binding.value = { x: e.pageX, y: e.pageY }
    }
    document.onmouseup = _e => {
      dragEl.style.cursor = 'move'
      document.onmousemove = null
      document.onmouseup = null
    }
  }
}

// 查找目标 dom
type target =
  | string
  | {
      target: string
      move: string
    }
const findTargetDom = (el: HTMLElement, target: Partial<target>) => {
  const targetMap = DragMap.get(el)
  if (!targetMap) return
  if (targetMap.dragEl) return
  if (!target) {
    targetMap.dragEl = el
    targetMap.moveEL = el
    createEvent(el)
  } else {
    if (typeof target === 'string') {
      target = {
        target: target
      }
    }
    const tergetEl = el.querySelector(String(target.target))
    if (tergetEl) {
      if (target.move) {
        targetMap.moveEL = el.querySelector(target.move)
      } else {
        targetMap.moveEL = el
      }
      targetMap.dragEl = tergetEl
      createEvent(el)
    }
  }
}

const drag: Directive = {
  // 或事件监听器应用前调用
  created(el, binding, _vnode, _prevVnode) {
    DragMap.set(el, { binding })
    findTargetDom(el, binding.value)
  },
  // 在元素被插入到 DOM 前调用
  beforeMount() {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted() {},
  // 绑定元素的父组件更新前调用
  beforeUpdate() {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding) {
    findTargetDom(el, binding.value)
  },
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el) {
    DragMap.delete(el)
  },
  // 绑定元素的父组件卸载后调用
  unmounted() {}
}

export default drag
